package com.szl.group.config;


import com.szl.group.config.auth.UserDetailsServiceImpl;
import com.szl.group.config.auth.custom.*;
import com.szl.group.config.interceptor.CustomAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
@Order(2)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Resource
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Resource
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Resource
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Resource
    private CustomLogoutSuccessHandler customLogoutSuccessHandler;

    @Resource
    private CustomInvalidSessionStrategy customInvalidSessionStrategy;

    @Resource
    private UserDetailsServiceImpl userDetailsService;


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers("/file/**","/test/**","/image/code", "/admin/**", "/login.html", "/invalid").permitAll()
                // .antMatchers("/index").authenticated()
                .anyRequest().authenticated()
                /* .anyRequest()
                 .access("@rbacService.hasPermission(request,authentication)")*/
                .and()
                .formLogin() //这里必须要写formLogin()，不然原有的UsernamePasswordAuthenticationFilter不会出现，也就无法配置我们重新的UsernamePasswordAuthenticationFilter
                .loginPage("/login.html") // 自定义登录页面
                //.loginProcessingUrl("/login/self")
                //.defaultSuccessUrl("/index")
                //.failureUrl("/login.html")
                .successHandler(customAuthenticationSuccessHandler)//两者只能选其一 自定义登录过滤器之后，这个不在生效
                .failureHandler(customAuthenticationFailureHandler)
                .permitAll()
                .and()
                .logout()
                //.logoutUrl("/logout")  //退出登录
                //.logoutSuccessUrl("/login.html") // 退出登录成功返回的页面
                .logoutSuccessHandler(customLogoutSuccessHandler).permitAll()
                .and()
                .csrf().disable() //关闭跨站伪造请求攻击
                .exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint)
                .and()
                .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)  // 无权访问 JSON 格式的数据
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)// 仅在需要时创建会话（默认）
                //.invalidSessionStrategy(customInvalidSessionStrategy) //session过期处理器
                .invalidSessionUrl("/login.html")//session过期调用的url
                .sessionFixation().migrateSession()// 在身份验证时，会创建一个新的HTTP会话，旧的会话将失效，旧会话的属性将被复制。 默认使用，可删除
                .maximumSessions(1)
                // 当达到最大值时，是否保留已经登录的用户
                .maxSessionsPreventsLogin(false);
        //.expiredSessionStrategy(new CustomExpiredSessionStrategy()) // 当达到最大值时，旧用户被踢出后的操作


        http.cors();// 允许跨域访问
        //用重写的Filter替换掉原有的UsernamePasswordAuthenticationFilter
        http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }

    /**
     * 注册自定义的UsernamePasswordAuthenticationFilter
     */
    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        filter.setPostOnly(true);
        filter.setFilterProcessesUrl("/login/self"); //这个是登录 处理用户名和密码的请求地址
        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }


    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) {
        //将项目中静态资源路径开放出来
        web.ignoring().antMatchers(
                "/css/**", "/fonts/**", "/img/**", "/js/**", "/static/**",
                "/api/**", "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html/**", "/oauth/check_token"
        );
    }



/*
    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        ProviderManager manager = new ProviderManager(Arrays.asList(customAuthenticationProvider()));
        return manager;
    }
*/

/*    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() {
        CustomAuthenticationProvider myAuthenticationProvider = new CustomAuthenticationProvider();
        myAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        myAuthenticationProvider.setUserDetailsService(userDetailsService);
        return myAuthenticationProvider;
    }*/


}
